package chapter5;

// 某些类型的相关工具，封装在对应类下，我们需要引入这些类
import java.util.Objects;
import java.util.Arrays;
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // 一个对象变量可以指示多种实际类型的对象的现象，称为多态
        A a;
        a = new B(1);
        B b;
        b = (B) a; // 将超类对象强制转换为子类对象。如果二者不在一个*继承链*上，会报ERROR
        if(b instanceof A) System.out.println("b instanceof A"); // 可以
        if(a instanceof B) System.out.println("a instanceof B"); // 也可以。a引用的确实是个B对象
        if(new A(1) instanceof B) System.out.println("none"); // 不行
        
        Object obj;
        // 所有数组类型都拓展了Object类
        obj = new A[10];
        obj = new int[10]; // 基本类型数组

        System.out.println(Arrays.equals(new int[10], (int[])obj)); // 比较数组长度，然后对每个元素调equals
        // 数组覆盖了hashCode方法，效果是根据元素内容生成哈希码
        System.out.println(Arrays.hashCode(new int[10]) + " " + Arrays.hashCode(new int[10])); // 一样的

        var c1 = new C("a", 1);
        var c2 = new C("a", 1);
        System.out.println(c1.equals(c2)); // true
        System.out.println(c2.equals(c1)); // true
        System.out.println(c1.equals(null)); // false
        System.out.println(c1.hashCode());
        System.out.println(c1); // 调用toString
        // chapter5.C [name=a, id=1]

        // Java的数组开在堆上，允许在运行时确定大小
        // 更灵活地，我们可以使用ArrayList类。旧的Vector类已被淘汰
        ArrayList<A> aa = new ArrayList<A>(); // 构造一个*泛型数组列表*
        
        aa = new ArrayList<A>(10); // 初始化容量(不是初始化元素数量)
        aa.add(new A(1)); // 尾部追加
        aa.add(new A(2));
        aa.set(1, new B(3)); // Java没有运算符重载，所以不像C++有[]下标运算符
        aa.trimToSize(); // 调整容量至当前size，多余的空间由GC回收
        // 其他列表该有的方法都有，不再赘述
        for (int i = 0; i < aa.size(); i++) {
            System.out.println(aa.get(i)); 
        }
        for (var v: aa) { // for each循环
            System.out.println(v.getpv());
        }
        var ar = new A[aa.size()];
        aa.toArray(ar); // 拷贝所有元素给数组

        // 有时，需要将基本类型转换为对象。比如尖括号中的参数不能是基本类型
        // var list = new ArrayList<int>();
        // 8个基本类对应8个包装器。包装器类没有访问器方法，本身还是final，无法派生
        var list = new ArrayList<Integer>(); // 注意，由于整数被包装在对象里，效率远比int[]低
        list.add(1); // 自动包装
        System.out.println(list.get(0) + 1); // 自动拆箱
        // 装箱和拆箱是编译器的工作，在生成class的字节码时插入必要的方法调用
        Integer i = 5, j = Integer.valueOf(6);
        System.out.println(i == j); // 未定义行为。除非拆箱后的值不超过8位
        // 一些基本方法被放在包装器中
        System.err.println(Integer.parseInt("5", 10));
        
        System.out.println(max(1, 2, 3)); // 编译器将new int[]{1,2,3}传给方法

        Color c = Color.BlUE;
        // 所有枚举类继承自Enum，包含一些有用方法
        System.out.println(c); // Color.BlUE.toString()返回枚举值名称"BLUE"
        c = Enum.valueOf(Color.class, "RED"); // 将c设置为Color.RED
        Color[] cc = Color.values(); // 继承自Enum的方法，返回一个包含所有枚举值的数组
        for (Color color : cc) {
            System.out.println(color);
        }

    }

    // 变参方法
    public static int max(int... numbers) {
        int result = Integer.MIN_VALUE;
        for (int number : numbers) result = (number > result) ? number : result; // numbers的类型为int[]
        return result;
    }
}

class A {
    private int pv;
    protected int pt;

    public A(int _pv) {
        this.pv = _pv;
    }

    public int getpv() {
        return pv;
    }

    final public void fun1() { // 阻止继承
        return;
    }

}

// Java只有公共继承
class B extends A {
    // pv无法在子类被访问

    B(int _pv) {
        super(_pv); // A的默认构造器已删除，必须调用super()调用超类的构造器
        pt = 1; // protected级别允许被子类访问
    }

    @Override // 指示覆盖超类方法，以免我们写错方法签名
    public int getpv() {
        return super.getpv(); // super指示编译器调用超类方法，并不是引用父类
        // 通过*方法表*直接调用超类方法
    }
}

// final类的所有方法自动成为final方法
final class C {
    private String name;
    private int id;

    public C(String _name, int _id) {
        name = _name;
        id = _id;
    }

    // 比较两个C类对象是否相等。必须覆盖Object的equals方法(默认比较两个变量是否引用同一个对象)。
    @Override
    public boolean equals(Object otherObject) {
        if(this == otherObject) // 一个快速判断
            return true;
        
        if(otherObject == null)
            return false;

        // object方法getClass，返回类信息对象(每个类只有一个实例)
        if(getClass() != otherObject.getClass())
            return false;
        // 另一种方式，但这样做不满足自反性
        // if(!(otherObject instanceof A))
        //     return false;
        
        C other = (C)otherObject; // 到此才能确认otherObject是一个非null的C类对象

        return Objects.equals(name, other.name) // 不采用name.equals(other.name)，防止对象name为null
            && id == other.id;
    }

    // Object类的hashCode方法默认从对象储存地址得到哈希值
    // String类覆盖了hashCode方法，改为从字符串内容生成
    @Override
    public int hashCode() {
        return Objects.hash(name, id); // 这个工具方法可以对每个对象调hashCode，然后组合到一起
    }

    // Object类的toString方法会打印含类名和哈希码的字符串
    // 只要对象与一个字符串通过“+”连接，Java编译器就会自动调用toString方法
    @Override
    public String toString() {
        return getClass().getName() + " [" + "name=" + name + ", id=" + id + "]";
    }
}

// 抽象类
abstract class D {
    public abstract void fun(); // 抽象方法
    // 抽象方法充当着占位方法的角色
}

class E extends D {

    // 必须覆盖超类的抽象方法
    public void fun() {
        return;
    }

}

// 枚举类
enum Color {
    RED, GREEN(1), BlUE(2);
    // 创建三个实例，称为枚举值，此后不可能构造新的对象(构造器私有)。

    protected int num;
    // 枚举类可以有构造器，且必须是私有的
    Color(){} // 默认私有
    private Color(int _num) {
        num = _num;
    }
    public int getNum() {
        return num;
    }
}